<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>大文件异步上传</title>
    <style type="text/css">
        progress {
            background-color: #56BE64;
        }

        progress::-webkit-progress-bar {
            background: #ccc;
        }

        progress::-webkit-progress-value {
            background: #56BE64;
        }

        percentage {
            position: fixed;
            left: 260px;
        }
    </style>
    <link rel="stylesheet" th:href="@{/webjars/layui/2.5.7/css/layui.css}">
</head>
<body>
<form id="uploadForm" method="post" action="/File/upload" enctype="multipart/form-data">
    <div>
        <input type="button" value="选择文件" onclick="javascript:$('input[name=\'filelist\']').click();"/>
        <input type="file" multiple="multiple" id="files" name="filelist" style="display: none;"
               onchange="uploadList()"/>
    </div>
    <input type="button" value="上传" id="btnUpload" onclick="isUpload()">
    <div id="msg" style="display: none"></div>
</form>
<div style="text-align: center">
    <table id="fileDiv" border="1" style="margin: auto" width="60%">
        <tr>
            <th>文件名</th>
            <th>进度条</th>
        </tr>
    </table>
</div>
<script th:src="@{/webjars/layui/2.5.7/layui.js}"></script>
<script>
    layui.use('element', function () {
        var element = layui.element;
        window.uploadList = function () {
            var files = document.getElementById("files").files;
            var fileList = "<tr>";
            for (var i = 0; i < files.length; i++) {
                var file = files[i].name;
                fileList += "<tr><td>" + file + "</td>";
                fileList += "<td style=\"width: 60%;height: 100px\"><div> <span style='width: 100px; display: inline-block; text-align: right'>上传进度：</span> " +
                    "<div class='layui-progress layui-progress-big' lay-showPercent='true' lay-filter='progress" + i + "'>" +
                    "<div class='layui-progress-bar layui-bg-red' lay-percent='0%'></div>" +
                    "</div>" +
                    "<span id='percentage" + i + "'>0%</span>" +
                    "</div>";
                fileList += "<div>" +
                    "<span style='width: 100px; display: inline-block; text-align: right'>上传速度：</span>" +
                    "<span style='width: 300px;' class='progress-speed' id='speed" + i + "'>0 M/S, 0/0M</span>" +
                    "</div>" +
                    "</td></tr>";
            }
            $("#fileDiv").append(fileList);
        }

        window.isUpload = function () {
            var files = document.getElementById("files").files;
            for (let i = 0; i < files.length; i++) {
                let file = files[i];
                let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
                let spark = new SparkMD5();
                let chunk_size = 2 * 1024 * 1024;
                let chunks = Math.ceil(file.size / chunk_size);
                let currentChunk = 0;
                let fileReader = new FileReader();

                function loadNext() {
                    let start = currentChunk * chunk_size,
                        end = start + chunk_size >= file.size ? file.size : start + chunk_size;

                    fileReader.readAsBinaryString(blobSlice.call(file, start, end));
                }

                fileReader.onload = function (e) {
                    spark.appendBinary(e.target.result);                 // append array buffer
                    currentChunk += 1;

                    if (currentChunk < chunks) {
                        loadNext();
                    } else {
                        let strmd5 = spark.end();
                        console.log("file = " + file.name + "strmd5 = " + strmd5)
                        let formData = new FormData();
                        formData.append("filemd5", strmd5);
                        formData.append("chunkNum", chunks);
                        $.ajax({
                            type: "POST",
                            async: true,
                            url: '/File/isUpload',
                            data: formData,
                            contentType: false,
                            cache: false,
                            processData: false,
                            error: function () {
                                console.log("error");
                            },
                            success: function (data) {
                                if (data.flag == 0) {
                                    console.log("文件未上传");
                                    upload(file, strmd5, i); // 开始分块上传
                                } else if (data.flag == 1) {
                                    console.log("文件已上传");
                                    // 执行进度条秒传
                                    element.progress('progress' + i, 100 + '%');
                                    $("#percentage" + i).html("100%");
                                    $('#speed' + i).html("极速秒传");
                                } else if (data.flag == 2) {
                                    console.log("已上传部分");
                                    uploadPart(file, strmd5, i, data.unUploadedList);
                                }
                            },
                        });
                    }
                }
                loadNext();
            }
        }

        function upload(file, filemd5, progressId) {
            let filename = file.name,   // 文件名
                fileSize = file.size,   // 文件总大小
                chunkSize = 1 * 1024 * 1024, // 分块为1MB
                chunkNum = Math.ceil(fileSize / chunkSize), // 总片数
                percent = 1 / chunkNum * 100,
                currentPercent = 0,
                loaded = 0;
            for (let i = 0; i < chunkNum; i++) {
                let start = i * chunkSize,
                    end = Math.min(fileSize, start + chunkSize);
                let formData = new FormData();
                formData.append("fileData", file.slice(start, end));
                formData.append("filemd5", filemd5);
                formData.append("filename", filename);
                formData.append("fileSize", fileSize);
                formData.append("chunkNum", chunkNum);
                formData.append("index", i + 1);

                let fileData = file.slice(start, end);
                let fileReader = new FileReader();
                let spark = new SparkMD5();
                let startTime = new Date().getTime();
                fileReader.readAsBinaryString(fileData);
                fileReader.onload = function (e) {
                    spark.appendBinary(e.target.result);
                    let chunkMd5 = spark.end();
                    formData.append("chunkMd5", chunkMd5);

                    $.ajax({
                        url: "File/bigUpload",
                        type: "POST",
                        data: formData,
                        processData: false,
                        contentType: false,
                        success: function (data) {
                            if (data.flag == 0) {
                                console.log("当前文件：" + filename + "，所有文件分块已上传完毕");
                                element.progress('progress' + progressId, 100 + '%');
                                $("#percentage" + progressId).html("100%");
                                if (loaded == 0)
                                    loaded = fileSize;
                                let time = ((new Date().getTime() - startTime) / 1000).toFixed(3);
                                $('#speed' + progressId).html(((loaded / 1024) / 1024 / time).toFixed(2) + "M/S, " + ((loaded / 1024) / 1024).toFixed(2) + "/" + ((fileSize / 1024) / 1024).toFixed(2) + "MB.");
                                return;
                                // 进度条到底并上传完成
                            } else if (data.flag == 1) {
                                console.log("当前文件:" + filename + " 分块" + (i + 1) + "已上传");
                                // 当前进度条前进一块
                                currentPercent = currentPercent + percent;
                                loaded = loaded + (currentPercent / 100) * fileSize;
                                element.progress('progress' + progressId, currentPercent + '%');
                                $("#percentage" + progressId).html(currentPercent + '%');

                                let time = ((new Date().getTime() - startTime) / 1000).toFixed(3);
                                $('#speed' + progressId).html(((loaded / 1024) / 1024 / time).toFixed(2) + "M/S, " + ((loaded / 1024) / 1024).toFixed(2) + "/" + ((fileSize / 1024) / 1024).toFixed(2) + "MB.");
                            } else if (data.flag == 2) {
                                console.log("此前分块未上传，现已上传文件：" + filename + " 分块" + (i + 1));
                                // 当前进度条前进一块
                                currentPercent = currentPercent + percent;
                                loaded = loaded + (currentPercent / 100) * fileSize;
                                element.progress('progress' + progressId, currentPercent + '%');
                                $("#percentage" + progressId).html(currentPercent + '%');

                                let time = ((new Date().getTime() - startTime) / 1000).toFixed(3);
                                $('#speed' + progressId).html(((loaded / 1024) / 1024 / time).toFixed(2) + "M/S, " + ((loaded / 1024) / 1024).toFixed(2) + "/" + ((fileSize / 1024) / 1024).toFixed(2) + "MB.");
                            }
                        }, error: function () {
                            console.log("服务器错误，分块上传失败，请重试！");
                        }
                    })
                }
            }
        }

        function uploadPart(file, filemd5, progressId, unUploaded) {
            let filename = file.name,   // 文件名
                fileSize = file.size,   // 文件总大小
                chunkSize = 1 * 1024 * 1024, // 分块为1MB
                chunkNum = Math.ceil(fileSize / chunkSize), // 总片数
                percent = 1 / chunkNum * 100,
                currentPercent = (chunkNum - unUploaded.length) * percent,
                loaded = (chunkNum - unUploaded.length) * chunkSize;
            for (let j = 0; j < unUploaded.length; j++) {
                let i = unUploaded[j] - 1;
                let start = i * chunkSize,
                    end = Math.min(fileSize, start + chunkSize);
                let formData = new FormData();
                formData.append("fileData", file.slice(start, end));
                formData.append("filemd5", filemd5);
                formData.append("filename", filename);
                formData.append("fileSize", fileSize);
                formData.append("chunkNum", chunkNum);
                formData.append("index", i + 1);

                let fileData = file.slice(start, end);
                let fileReader = new FileReader();
                let spark = new SparkMD5();
                let startTime = new Date().getTime();
                fileReader.readAsBinaryString(fileData);
                fileReader.onload = function (e) {
                    spark.appendBinary(e.target.result);
                    let chunkMd5 = spark.end();
                    formData.append("chunkMd5", chunkMd5);

                    $.ajax({
                        url: "File/bigUpload",
                        type: "POST",
                        data: formData,
                        processData: false,
                        contentType: false,
                        success: function (data) {
                            if (data.flag == 0) {
                                console.log("当前文件：" + filename + "，所有文件分块已上传完毕");
                                element.progress('progress' + progressId, 100 + '%');
                                $("#percentage" + progressId).html("100%");
                                if (loaded == 0)
                                    loaded = fileSize;
                                let time = ((new Date().getTime() - startTime) / 1000).toFixed(3);
                                $('#speed' + progressId).html(((loaded / 1024) / 1024 / time).toFixed(2) + "M/S, " + ((loaded / 1024) / 1024).toFixed(2) + "/" + ((fileSize / 1024) / 1024).toFixed(2) + "MB.");
                                return;
                                // 进度条到底并上传完成
                            } else if (data.flag == 1) {
                                console.log("当前文件:" + filename + " 分块" + (i + 1) + "已上传");
                                // 当前进度条前进一块
                                currentPercent = currentPercent + percent;
                                loaded = loaded + (currentPercent / 100) * fileSize;
                                element.progress('progress' + progressId, currentPercent + '%');
                                $("#percentage" + progressId).html(currentPercent + '%');

                                let time = ((new Date().getTime() - startTime) / 1000).toFixed(3);
                                $('#speed' + progressId).html(((loaded / 1024) / 1024 / time).toFixed(2) + "M/S, " + ((loaded / 1024) / 1024).toFixed(2) + "/" + ((fileSize / 1024) / 1024).toFixed(2) + "MB.");
                            } else if (data.flag == 2) {
                                console.log("此前分块未上传，现已上传文件：" + filename + " 分块" + (i + 1));
                                // 当前进度条前进一块
                                currentPercent = currentPercent + percent;
                                loaded = loaded + (currentPercent / 100) * fileSize;
                                element.progress('progress' + progressId, currentPercent + '%');
                                $("#percentage" + progressId).html(currentPercent + '%');

                                let time = ((new Date().getTime() - startTime) / 1000).toFixed(3);
                                $('#speed' + progressId).html(((loaded / 1024) / 1024 / time).toFixed(2) + "M/S, " + ((loaded / 1024) / 1024).toFixed(2) + "/" + ((fileSize / 1024) / 1024).toFixed(2) + "MB.");
                            }
                        }, error: function () {
                            console.log("服务器错误，分块上传失败，请重试！");
                        }
                    })
                }
            }
        }
    });
</script>
<script th:src="@{/webjars/jquery/3.3.1/jquery.min.js}"></script>
<script th:src="@{/webjars/spark-md5/3.0.0/spark-md5.min.js}"></script>
</body>
</html>